using System;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Render.Maths;
using Beaker.OpenCube.Textures;

namespace Beaker.OpenCube.Render.Materials
{
	public sealed class ImageMaterial
		: Material
	{
		private readonly ITexelSource _texelSource;
		private readonly IMapping _mapping;

		public ImageMaterial( ITexelSource texelSource, IMapping mapping = null )
		{
			Contract.Requires<ArgumentNullException>( texelSource != null );
			_texelSource = texelSource;
			_mapping = mapping;
		}

		public Color Diffuse( Intersection intersection )
		{
			Point2 localHitPoint =
				new Point2(
					intersection.LocalHitPoint.X, // u
					intersection.LocalHitPoint.Y // v
				);

			// either use mapping, or 
			Point2 texelCoordinates;
			if( _mapping != null )
				texelCoordinates = _mapping.GetTexelCoordinates( localHitPoint, _texelSource.Size );
			else
			{
				double u = (int)( intersection.LocalHitPoint.X * ( _texelSource.Size.Width - 1 ) ) % _texelSource.Size.Width;
				double v = (int)( intersection.LocalHitPoint.Y * ( _texelSource.Size.Height - 1 ) ) % _texelSource.Size.Height;

				// modules also can give negative values, so when we've got them
				// offset upwards to get a positive value
				if( u < 0 )
					u += _texelSource.Size.Width;
				if( v < 0 )
					v += _texelSource.Size.Height;

				texelCoordinates = new Point2( u, v );
			}

			// get texel a local coordinates
			return
				_texelSource
					.GetTexelColor( texelCoordinates.X, texelCoordinates.Y );

		}

		public Color Specular( Intersection intersection )
		{
			return Color.White;
		}

		public double Reflect( Intersection intersection )
		{
			return 0d;
		}

		public double Roughness( Intersection intersection )
		{
			return 250;
		}
	}

	public static class BitmapExtension
	{
		public static Bitmap ToBitmap( this Texture texture )
		{
			return
				new Bitmap(
					texture.Image,
					texture.GetRectangleForImageIndex( 0 )
			);
		}
	}

}